#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <log.h>
#include <cliManager.h>
#include <framework.h>
#include <cnotify.h>
#include <lvos.h>
#include <utils.h>
#include <commands.h>
#include <SessionManager.h>

namespace cli
{
    #undef CURRENT_LOG_ID
    LOCAL int cli_module_logId = 0;
    #define CURRENT_LOG_ID cli_module_logId
    
    STATUS cliManager::parse(const std::string & cmdLine, std::string& cmdName, std::vector<std::string>& params)
    {
        cmdName.clear();
        params.clear();

        if (cmdLine.empty())
        {
            return RET_ERROR;
        }

        std::vector<std::string> tokens = utils::split(cmdLine);

        if (!tokens.empty()) 
        {
            cmdName = tokens[0];
            params = std::vector<std::string>(tokens.begin() + 1, tokens.end());
        }
        if (cmdName.empty())
        {
            return RET_ERROR;
        }

        return OK;
    }
    STATUS cli::cliManager::dispatch(SessionPtr session)
    {
        STATUS retVal = OK;
        std::string prompt = session->getUsrPrompt();

        if (!session->isReadable())
        {
            retVal = dispatchInternal(session);
            prompt = utils::format("value = %d = 0x%x" ENDLS, retVal, retVal);
            prompt.append(session->getUsrPrompt());
        }

        LVOS_Write(prompt, session);
        
        return retVal;
    }
    STATUS cli::cliManager::dispatchInternal(SessionPtr session)
    {
        std::string cmdStr;
        std::vector<std::string>cmdList;
        STATUS retVal = OK;

        session->extractRowData(cmdStr);
        cmdList = utils::split(cmdStr, ENDLS);
        for (auto & cmdStr : cmdList)
        {
            retVal |= dispatchInternal(cmdStr, session);
        }
        return retVal;
    }
    STATUS cliManager::dispatchInternal(std::string& cmdStr, SessionPtr session)
    {
        std::string cmdName;
        std::vector<std::string> params;

        LOG_INFO("[CLI_INPUT]%s", cmdStr.c_str());

        COND_CHECK_RET_ERROR(parse(cmdStr, cmdName, params) != OK);

        auto pCmd = cliGetCmd(cmdName);
        COND_CHECK_RET_ERROR(pCmd == nullptr);

        if ((m_dispatchCallback != nullptr) && (m_dispatchCallback(pCmd->pCmdDesc, session) == OK))
        {
            return OK;
        }

        ArgList message;
        TextTable outmessage('-', SPACE, '-');

        if (pCmd->pCmdInst->parse(params, message) != OK)
        {
            LVOS_Write(pCmd->pCmdInst->getLastError() + ENDLS, session);
            return RET_ERROR;
        }

        if (message.has("help"))
        {
            std::ostringstream oss;
            pCmd->pCmdInst->print_help(oss);
            LVOS_Write(oss, session);
            return OK;
        }

        COND_CHECK_RET_ERROR((GET_FRAMEWORK_MGR->callObjectMethod<Cnotify, STATUS>(
            MODULE_NOTIFY,
            &Cnotify::notify,
            static_cast<int>(MODULE_CLI_MGR),
            static_cast<int>(pCmd->pCmdDesc->recvId),
            static_cast<int>(pCmd->pCmdDesc->cmdId),
            message, outmessage,
            static_cast<bool>(pCmd->pCmdDesc->is_async)) != OK));

        LVOS_Write(outmessage, session);

        return OK;
    }
    STATUS  cliManager::cliCmdRegister(const cli::CliCmd* cmds, size_t count)
    {
        for (size_t i = 0; i < count; i++)
        {
            const cli::CliCmd* pCliCmd = &cmds[i];

            auto pcmdDes = std::make_shared<CmdDesc>(pCliCmd, pCliCmd->name, pCliCmd->description);

            COND_CHECK_RET_ERROR((pcmdDes == nullptr));
            if (pcmdDes->pCmdInst == nullptr)
            {
                LOG_ERROR("cliCmdRegister failed, cmd: %s",  pCliCmd->name);
                return RET_ERROR;
            }

            if (pcmdDes->pCmdInst->add_params(pCliCmd->params) != OK)
            {
                LOG_ERROR("cliCmdRegister failed, cmd: %s",  pCliCmd->name);
                return RET_ERROR;
            }

            std::lock_guard<std::mutex> lock(m_objectLock);
            m_cmdList[pCliCmd->name] = pcmdDes;
        }

        return OK;
    }

    STATUS  cliManager::cliDelCmd(std::string& cmd)
    {
        auto pCmd = cliGetCmd(cmd);

        COND_CHECK_RET_ERROR((pCmd == nullptr));

        LOG_TRACE("cliDelCmd, cmd: %s", cmd);

        std::lock_guard<std::mutex> lock(m_objectLock);
        m_cmdList.erase(cmd);

        return OK;
    }
    STATUS cliManager::cliSetDispatchHook(FUNCPTR callback)
    {
        m_dispatchCallback = callback;

        return OK;
    }
    cliManager::CmdDescPtr cliManager::cliGetCmd(std::string& cmd)
    {
        std::lock_guard<std::mutex> lock(m_objectLock);
        auto it = m_cmdList.find(cmd);

        COND_CHECK_RET_VAL((it == m_cmdList.end()), nullptr);

        return it->second;
    }

    STATUS  cliManager::dump(TextTable &t)
    {
        return OK;
    }
    PROCESS_IMPL_DESC(cliManager, process)
    {
        PROCESS_BEGIN()
        PROCESS_CALL(CMD_SYS_SHUTDOWN, sysShutdown)
        PROCESS_END(RET_ERROR)
    }

    PROCESS_IMPL_DESC(cliManager, sysShutdown)
    {
        return destory();
    }
    STATUS  cliManager::preInit()
    {
        m_dispatchCallback = nullptr;

        COND_CHECK_RET_ERROR(Ilog::logModuleRegister(cli_module_logId, "climgr.log") != OK);

        return OK;
    }
    LOCAL void cliRecvProc(SessionPtr session)
    {
        COND_CHECK_RET_VAL(session == nullptr, );
           GET_FRAMEWORK_MGR->callObjectMethod<cliManager, STATUS>
               (MODULE_CLI_MGR, &cliManager::dispatch, static_cast<SessionPtr>(session));
    }

    STATUS  cliManager::postInit()
    {
        COND_CHECK_RET_ERROR(registerMessageListener(MODULE_CLI_MGR) != OK);
        return GET_FRAMEWORK_MGR->callObjectMethod<SessionManager, STATUS>
            (MODULE_TELNET_MGR, &SessionManager::setReadCallback, reinterpret_cast<FUNCPTR>(cliRecvProc));
    }

    STATUS cliManager::destory()
    {
        std::lock_guard<std::mutex> lock(m_objectLock);
        m_cmdList.clear();
        return OK;
    }
    REG_TO_FRAMEWORK(cliManager, MODULE_CLI_MGR);
}
